package client_server.IO;


import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Observable;
import java.util.Observer;

/**
 * NetworkIO creates the thread for incoming and outgoing communications.
 * @author Martin Nume
 *
 */
public class NetworkIO implements Runnable, Observer{
	private Thread thread;
	public static enum State {CONNECT, STANDBY}
	//state of the thread
	private State state;
	private InputOutputDataThread inputOutput;
	//ip to the server
	private final String SERVER_IP;
	//port which the server is listening on
	private final int SERVER_WELCOME_PORT;
	private Socket socket;
	private PrintWriter out;
	
	private HandleServerData handler;
	
	private String command;
	
	
	public NetworkIO(HandleServerData handler) {
		this.handler = handler;
		inputOutput = null;
		thread = new Thread(this);
		state = State.STANDBY;
		SERVER_IP = "46.239.112.111";
		SERVER_WELCOME_PORT = 7000;
		socket = null;
		thread.start();
	}
	/**
	 * Change the state of this thread
	 * @param state
	 */
	private void changeState(State state) {
		this.state = state;
	}
	/**
	 * Adds data to the outgoing buffer
	 * @param data
	 */
	public void sendData(String data) {
		
		inputOutput.addToOutBuffer(data);
	}
	
	/**
	 * Set the command for retrieving the list from the server
	 */
	public void getGameList() {
		command = "GET_SERVERLIST";
		changeState(State.CONNECT);
	}
	
	/**
	 * Set the command for join a game
	 * @param data
	 */
	public void joinGame(String data) {
		String[] dataStr = data.split(";");  // Format: "gameId;gameName;nrOfJoined;maxPlayers"
		command = "JOIN_GAME;" + dataStr[0] + ";" + dataStr[1];	 // "JOIN_GAME;gameId;playerName"
		changeState(State.CONNECT);
	}
	
	/**
	 * Set the command for host a game
	 * @param gameName
	 */
	public void hostGame(String gameName) {
		command = "HOST_GAME;" + gameName + ";2;MyPlayerName";
		changeState(State.CONNECT);
	}
	
	@Override
	public void run() {
		while(true) {
				switch(state) {
					case CONNECT:
					{
						//Creates a socket to the servers welcomeport
						createSocket();
						//if the outputstream is null.. 
						if(out == null){
							state = State.STANDBY;
							//we cant do anything. set to standby
							continue;
						}
						//send the command to the server
						out.println(command);
						out.flush();
						//if we already have a thread running... stop it.
						if( inputOutput !=null)
							inputOutput.stopThread();
						inputOutput = new InputOutputDataThread(socket);
						//add an observer
						inputOutput.addObserver(this);
						//start thread
						inputOutput.startThread();
						
						state = State.STANDBY;
						
						break;
					}
					
					case STANDBY:
					{
						break;
					}
				}
		}
	}
	/**
	 * Creates a socket to connect to the server
	 */
	private void createSocket(){
		try{
			
			socket =null;
			out = null;
			socket = new Socket(SERVER_IP, SERVER_WELCOME_PORT);
			out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		}
		catch(UnknownHostException es){
			System.out.println("Verify ur internetconnection and the ip&port to the server");
			es.printStackTrace();
		}
		catch(IOException e){
			System.out.println("Could not connect to server on port: " + SERVER_WELCOME_PORT);
			e.printStackTrace();
		}
	}
	/**
 	* Method that are called when the Observable objects calls notify(). Here we know that we have read data from the server
 	*/
	@Override
	public void update(Observable arg0, Object arg1) {
		if(arg1 instanceof String){
			System.out.println("from server, thru InputDATAthread: "  + (String)arg1);
			
			handler.handleServerData((String)arg1);
		}
		
	}
	/**
	 * Close the connections
	 */
	public void closeConnections() {
		if(inputOutput != null) {
			inputOutput.stopThread();
		}
	}

}
